-- | Dieses Modul implementiert einen interaktiven Interpreter fuer Concurrent Haskell with Futures

import System.IO																					-- benoetigt fuer hFlush
import Control.Exception(try,IOException,SomeException,evaluate)									-- benoetigt fuer evaluate/try/...
import Data.IORef																					-- benoetigt fuer IORef
import CHF.Run as CHF																				-- Concurrent Haskell with Futures

----------------------------------------------------------------------------------------------------------
-- | 'getInputString' Wrapper fuer getLine. (Liefert auf jeden Fall einen String.)
--   Als Parameter kann eine Eingabaufforderung Uebergeben werden. Der Trick mit hFlush aus System.IO
--   stammt von http://www.haskell.org/pipermail/haskell/2006-September/018430.html
--   und sorgt dafuer, dass sich das Programm in GHCI genauso verhaellt, wie die kompilierte .exe-Datei!
----------------------------------------------------------------------------------------------------------
getInputString :: String -> IO (String)
getInputString s = putStr s >> hFlush stdout >> getLine


----------------------------------------------------------------------------------------------------------
-- | 'usage' gibt eine Hilfe-Mitteilung mit allen zur Verfuegung stehenden Kommandos aus.
----------------------------------------------------------------------------------------------------------
usage :: IO ()
usage = let
			txt = "Moegliche Kommandos sind:\n"
					++ " :quit\t\tzum Beenden\n"
					++ " :help\t\tfuer Hilfe\n"
					++ " :define\tzum Definieren globaler data-Definitionen\n"
					++ " :clear\tzum Loeschen globaler data-Definitionen\n"
					++ " :runfile\tum eine Datei auszufuehren\n"
		in putStrLn txt


----------------------------------------------------------------------------------------------------------
-- | 'run' gibt Begruessung aus und startet den interaktiven Interpreter
----------------------------------------------------------------------------------------------------------
run :: IO ()
run = do
			putStrLn "Concurrent Haskell with Futures (CHF) [Nhan/Leppin]"
			putStrLn "===================================================="
			putStrLn "Beenden :quit / Hilfe :help"
			ref <- newIORef ""							-- neue IORef erzeugen mit initialem leeren String
			runInterpr ref True

			
----------------------------------------------------------------------------------------------------------
-- | 'evalExpr' probiert, den Eingabausdruck inp auszuwerten und faengt ggf. Fehler ab.
----------------------------------------------------------------------------------------------------------
evalExpr :: String -> IO ()
evalExpr inp = do
					r <- try (CHF.runCM1 inp) :: IO (Either SomeException ())
					case r of
						Left ex -> putStr "CHF Fehler: " >> print ex >> putStrLn "\n"
						Right val -> return ()		-- runCM1 liefert keine Rueckgabe, sondern prodzuiert print-Seiteneffekt


----------------------------------------------------------------------------------------------------------
-- | 'runInterpr' ist die eigentliche interaktive Interpreterfunktion
--   wenn der Parameter auf False gesetzt wird, wird sie beendet, solang er True ist, laeuft die Schleife.
----------------------------------------------------------------------------------------------------------
runInterpr :: IORef String -> Bool -> IO ()
runInterpr _ False = putStrLn "So long, and thanks for all the fish."
runInterpr ref True = do 
						cmd <- getInputString "*CHF>"
						case cmd of
							"" 			-> runInterpr ref True					-- bei Enter machen wir nix
							":quit"		-> runInterpr ref False					-- Quit (set to False)
							":define"	-> do	-- data-Block speichern
												s <- readIORef ref
												putStr "aktuelle Definitionen: \"" >> putStr s >> putStrLn "\""
												s' <- getInputString "neue Definition (Enter=Abbruch): "
												if null s' then do	-- tu nichts
																	putStrLn "abgebrochen."
																	runInterpr ref True
													else do -- speichere neuen Wert
															writeIORef ref s'
															putStr "neue Definitionen \"" >> putStr s' >> putStrLn "\" gespeichert."
															runInterpr ref True
							":clear"	-> do	-- data-Block loeschen
												writeIORef ref ""
												putStrLn "Data-Definitionen geloescht!"
												runInterpr ref True
							":runfile"	-> do	-- Datei einlesen und ausfuehren
												fpath <- getInputString "Datei: "
												f <- catch (readFile fpath) (\e -> do	let err = show (e :: IOException)
																						in putStr ("Fehler beim Oeffnen der Datei " ++ err ++ "\n\n") >> return "")
												if f /= "" then evalExpr f >> runInterpr ref True else runInterpr ref True			-- nur auswerten, wenn auch was zum auswerten da ist...
							":help"		-> do	-- Hilfe anzeigen
												usage
												runInterpr ref True
							otherwise	-> do 	-- Eingabe auswerten
												datas <- readIORef ref
												putStrLn ("Auswertung von \"" ++ (buildExpr datas cmd) ++ "\"...")
												evalExpr (buildExpr datas cmd)
												runInterpr ref True

												
----------------------------------------------------------------------------------------------------------
-- | 'buildExpr' baut aus dem Data- und Expressionanweisungen einen vollstaendigen Ausdruck zusammen	
----------------------------------------------------------------------------------------------------------
buildExpr :: String -> String -> String
buildExpr d e = if d == "" then "expression " ++ e
					else d ++ " expression " ++ e						
												
												
----------------------------------------------------------------------------------------------------------
-- | 'main' ist die main-funktion fuer den haskell-compiler. Sie ruft den interaktiven Interpreter auf.			
----------------------------------------------------------------------------------------------------------
main = run